package com.dsl.dslsuzhou.service;

import com.dsl.dslsuzhou.model.dto.CameraDto;
import com.dsl.dslsuzhou.model.dto.CameraPageRequest;
import com.dsl.dslsuzhou.model.dto.CameraPageResponse;
import com.dsl.dslsuzhou.model.dto.DeviceQueryRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.stream.Collectors;

/**
 * 摄像头分页优化服务
 * 支持兼容性设计：既支持传统的devIds参数，也支持新的业务条件参数
 */
@Service
@Slf4j
public class CameraPageOptimizedService {

    @Autowired
    private CameraService cameraService;

    @Autowired
    private DeviceService deviceService;

    // 批次配置
    @Value("${camera.batch.base-size:150}")
    private int baseBatchSize;

    @Value("${camera.batch.max-concurrent:3}")
    private int maxConcurrentBatches;

    @Value("${camera.batch.timeout-seconds:10}")
    private int timeoutSeconds;

    // 并发控制
    private Semaphore batchSemaphore;

    @PostConstruct
    public void init() {
        this.batchSemaphore = new Semaphore(maxConcurrentBatches);
        log.info("摄像头分页服务初始化完成: batchSize={}, maxConcurrent={}, timeout={}s",
                baseBatchSize, maxConcurrentBatches, timeoutSeconds);
    }

    /**
     * 兼容性分页查询摄像头
     * 支持两种模式：1) 传统devIds模式  2) 业务条件模式
     */
    public Mono<CameraPageResponse> getCameraPageOptimized(String category, String area, String building, String floor,
            Integer current, Integer size, Integer status, String token) {

        long startTime = System.currentTimeMillis();

        return Mono.fromCallable(() -> {
                    List<String> devIds = getDeviceIdsByConditions(category, area, building, floor);
                    // 1. 参数优先级判断：devIds > 业务条件 > 默认条件
                    if (!CollectionUtils.isEmpty(devIds)) {
                        log.info("使用兼容模式: 直接使用传入的设备ID, count={}", devIds.size());
                        return devIds;
                    }

                    // 2. 检查是否有业务条件参数
                    if (hasBusinessConditions(category, area, building, floor)) {
                        log.info("使用优化模式: 根据业务条件查询设备ID");
                        return getDeviceIdsByConditions(category, area, building, floor);
                    }

                    // 3. 都没有则查询默认条件（所有视频监控设备）
                    log.info("使用默认模式: 查询所有视频监控设备");
                    return getDeviceIdsByConditions("通用视频服务设备", null, null, null);
                })
                .flatMap(deviceIds -> {
                    if (deviceIds.isEmpty()) {
                        log.info("未找到符合条件的设备，返回空分页结果");
                        return Mono.just(createEmptyPage(current, size, 0));
                    }

                    // 统一的分页处理逻辑
                    return getCameraPageByDeviceIds(deviceIds, current, size, status, token);
                })
                .doOnSuccess(response -> {
                    long duration = System.currentTimeMillis() - startTime;
                    log.info("摄像头分页查询完成: total={}, records={}, duration={}ms",
                            response.getTotal(), response.getRecords().size(), duration);
                })
                .doOnError(error -> {
                    long duration = System.currentTimeMillis() - startTime;
                    log.error("摄像头分页查询失败: duration={}ms, error={}", duration, error.getMessage(), error);
                });
    }

    /**
     * 检查是否有业务条件参数
     */
    private boolean hasBusinessConditions(String category, String area, String building, String floor) {
        return StringUtils.hasText(category) || StringUtils.hasText(area) ||
                StringUtils.hasText(building) || StringUtils.hasText(floor);
    }

    /**
     * 根据业务条件查询设备ID（带缓存）
     */
    @Cacheable(value = "device-ids",
            key = "#category + '_' + #area + '_' + #building + '_' + #floor",
            unless = "#result.isEmpty()")
    public List<String> getDeviceIdsByConditions(String category, String area,
                                                 String building, String floor) {
        log.debug("从数据库查询设备ID: category={}, area={}, building={}, floor={}",
                category, area, building, floor);

        DeviceQueryRequest request = new DeviceQueryRequest();
        request.setCategory(StringUtils.hasText(category) ? category : "通用视频服务设备");
        request.setArea(area);
        request.setBuilding(building);
        request.setFloor(floor);

        return deviceService.getDeviceIds(request);
    }

    /**
     * 基于设备ID进行分页查询（设备级分页）
     */
    private Mono<CameraPageResponse> getCameraPageByDeviceIds(
            List<String> allDeviceIds, Integer current, Integer size, Integer status, String token) {

        // 1. 在设备ID层面进行分页
        int totalDevices = allDeviceIds.size();
        int offset = (current - 1) * size;
        int endIndex = Math.min(offset + size, totalDevices);

        if (offset >= totalDevices) {
            // 超出范围，返回空页
            log.info("分页超出范围: offset={}, totalDevices={}", offset, totalDevices);
            return Mono.just(createEmptyPage(current, size, totalDevices));
        }

        // 2. 获取当前页需要的设备ID
        List<String> pageDeviceIds = allDeviceIds.subList(offset, endIndex);

        log.info("设备级分页: 总设备数={}, 当前页={}, 页大小={}, 当前页设备数={}",
                totalDevices, current, size, pageDeviceIds.size());

        // 3. 分批调用第三方接口获取摄像头数据
        return getCameraDataInBatches(pageDeviceIds, status, token)
                .map(cameraList -> {
                    // 4. 构建分页响应
                    CameraPageResponse response = new CameraPageResponse();
                    response.setRecords(cameraList);
                    response.setTotal((long) totalDevices);
                    response.setCurrent((long) current);
                    response.setSize((long) size);
                    response.setPages((long) Math.ceil((double) totalDevices / size));
                    return response;
                });
    }

    /**
     * 分批调用第三方接口获取摄像头数据
     */
    private Mono<List<CameraDto>> getCameraDataInBatches(
            List<String> deviceIds, Integer status, String token) {

        if (deviceIds.isEmpty()) {
            return Mono.just(new ArrayList<>());
        }

        // 1. 分批处理（避免URL过长）
        List<List<String>> batches = splitIntoBatches(deviceIds, baseBatchSize);

        log.info("分批调用第三方接口: 设备总数={}, 批次数={}", deviceIds.size(), batches.size());

        // 2. 并发调用各批次
        List<Mono<List<CameraDto>>> batchMonos = batches.stream()
                .map(batch -> callSingleBatch(batch, status, token))
                .collect(Collectors.toList());

        // 3. 合并所有批次结果
        return Flux.fromIterable(batchMonos)
                .flatMap(mono -> mono)
                .collectList()
                .map(lists -> lists.stream()
                        .flatMap(List::stream)
                        .collect(Collectors.toList()));
    }

    /**
     * 调用单个批次（带并发控制、重试、超时）
     */
    private Mono<List<CameraDto>> callSingleBatch(List<String> batchDeviceIds, Integer status, String token) {
        return Mono.fromCallable(() -> {
                    // 获取并发许可
                    batchSemaphore.acquire();
                    log.debug("获取批次处理许可: batchSize={}, 剩余许可={}",
                            batchDeviceIds.size(), batchSemaphore.availablePermits());
                    return batchDeviceIds;
                })
                .flatMap(deviceIds -> {
                    // 调用第三方接口获取一批设备的摄像头数据
                    // 使用足够大的页大小来获取所有数据
                    int maxPageSize = Math.max(deviceIds.size() * 2, 200);

                    CameraPageRequest request = new CameraPageRequest();
                    request.setDevIds(deviceIds);
                    request.setCurrent(1);
                    request.setSize(maxPageSize);
                    request.setStatus(status);

                    return cameraService.getCameraPage(request, token)
                            .map(response -> response.getRecords())
                            .timeout(Duration.ofSeconds(timeoutSeconds))
                            .retryWhen(Retry.backoff(2, Duration.ofMillis(500))
                                    .maxBackoff(Duration.ofSeconds(2)))
                            .onErrorReturn(new ArrayList<>()); // 出错时返回空列表
                })
                .doOnSuccess(cameras ->
                        log.debug("批次查询成功: batchSize={}, cameraCount={}", batchDeviceIds.size(), cameras.size()))
                .doOnError(error ->
                        log.warn("批次查询失败: batchSize={}, error={}", batchDeviceIds.size(), error.getMessage()))
                .doFinally(signal -> {
                    // 释放并发许可
                    batchSemaphore.release();
                    log.debug("释放批次处理许可: 剩余许可={}", batchSemaphore.availablePermits());
                });
    }

    /**
     * 创建空分页响应
     */
    private CameraPageResponse createEmptyPage(Integer current, Integer size, int totalDevices) {
        CameraPageResponse response = new CameraPageResponse();
        response.setRecords(new ArrayList<>());
        response.setTotal((long) totalDevices);
        response.setCurrent((long) current);
        response.setSize((long) size);
        response.setPages(totalDevices > 0 ? (long) Math.ceil((double) totalDevices / size) : 0L);
        return response;
    }

    /**
     * 将列表分批
     */
    private List<List<String>> splitIntoBatches(List<String> list, int batchSize) {
        List<List<String>> batches = new ArrayList<>();
        for (int i = 0; i < list.size(); i += batchSize) {
            int end = Math.min(i + batchSize, list.size());
            batches.add(list.subList(i, end));
        }
        return batches;
    }
}
